/*
 * Copyright © 2017 CHANGLEI. All rights reserved.
 */

package com.daimajia.slider.library.Indicators;

import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.support.v4.view.PagerAdapter;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.daimajia.slider.library.ISliderAdapter;
import com.daimajia.slider.library.Tricks.IInfinitePagerAdapter;
import com.daimajia.slider.library.Tricks.IViewPagerEx;
import net.izhuo.app.library.R;

import java.util.ArrayList;

/**
 * Pager Indicator.
 */
public class IPagerIndicator extends LinearLayout implements
		IViewPagerEx.OnPageChangeListener {

	private Context mContext;

	/**
	 * bind this Indicator with
	 * {@link com.daimajia.slider.library.Tricks.IViewPagerEx}
	 */
	private IViewPagerEx mPager;

	/**
	 * Variable to remember the previous selected indicator.
	 */
	private ImageView mPreviousSelectedIndicator;

	/**
	 * Previous selected indicator position.
	 */
	private int mPreviousSelectedPosition;

	/**
	 * Custom selected indicator style resource id.
	 */
	private int mUserSetUnSelectedIndicatorResId;

	/**
	 * Custom unselected indicator style resource id.
	 */
	private int mUserSetSelectedIndicatorResId;

	private Drawable mSelectedDrawable;
	private Drawable mUnselectedDrawable;

	/**
	 * This value is from {@link ISliderAdapter}
	 * getRealCount() represent
	 *
	 * the indicator count that we should draw.
	 */
	private int mItemCount = 0;

	private Shape mIndicatorShape = Shape.Oval;

	private IndicatorVisibility mVisibility = IndicatorVisibility.Visible;

	private int mDefaultSelectedColor;
	private int mDefaultUnSelectedColor;

	private float mDefaultSelectedWidth;
	private float mDefaultSelectedHeight;

	private float mDefaultUnSelectedWidth;
	private float mDefaultUnSelectedHeight;

	public enum IndicatorVisibility {
		Visible, Invisible;
	};

	private GradientDrawable mUnSelectedGradientDrawable;
	private GradientDrawable mSelectedGradientDrawable;

	private LayerDrawable mSelectedLayerDrawable;
	private LayerDrawable mUnSelectedLayerDrawable;

	private float mPadding_left;
	private float mPadding_right;
	private float mPadding_top;
	private float mPadding_bottom;

	private float mSelectedPadding_Left;
	private float mSelectedPadding_Right;
	private float mSelectedPadding_Top;
	private float mSelectedPadding_Bottom;

	private float mUnSelectedPadding_Left;
	private float mUnSelectedPadding_Right;
	private float mUnSelectedPadding_Top;
	private float mUnSelectedPadding_Bottom;

	/**
	 * Put all the indicators into a ArrayList, so we can remove them easily.
	 */
	private ArrayList<ImageView> mIndicators = new ArrayList<ImageView>();

	public IPagerIndicator(Context context) {
		this(context, null);
	}

	public IPagerIndicator(Context context, AttributeSet attrs) {
		super(context, attrs);

		mContext = context;

		final TypedArray attributes = context.obtainStyledAttributes(attrs,
				R.styleable.BoxPagerIndicator, 0, 0);

		int visibility = attributes.getInt(
				R.styleable.BoxPagerIndicator_visibility,
				IndicatorVisibility.Visible.ordinal());

		for (IndicatorVisibility v : IndicatorVisibility.values()) {
			if (v.ordinal() == visibility) {
				mVisibility = v;
				break;
			}
		}

		int shape = attributes.getInt(R.styleable.BoxPagerIndicator_shape,
				Shape.Oval.ordinal());
		for (Shape s : Shape.values()) {
			if (s.ordinal() == shape) {
				mIndicatorShape = s;
				break;
			}
		}

		mUserSetSelectedIndicatorResId = attributes.getResourceId(
				R.styleable.BoxPagerIndicator_selected_drawable, 0);
		mUserSetUnSelectedIndicatorResId = attributes.getResourceId(
				R.styleable.BoxPagerIndicator_unselected_drawable, 0);

		mDefaultSelectedColor = attributes.getColor(
				R.styleable.BoxPagerIndicator_selected_color,
				Color.rgb(255, 255, 255));
		mDefaultUnSelectedColor = attributes.getColor(
				R.styleable.BoxPagerIndicator_unselected_color,
				Color.argb(33, 255, 255, 255));

		mDefaultSelectedWidth = attributes.getDimension(
				R.styleable.BoxPagerIndicator_selected_width, (int) pxFromDp(6));
		mDefaultSelectedHeight = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_selected_height, (int) pxFromDp(6));

		mDefaultUnSelectedWidth = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_unselected_width, (int) pxFromDp(6));
		mDefaultUnSelectedHeight = attributes
				.getDimensionPixelSize(
						R.styleable.BoxPagerIndicator_unselected_height,
						(int) pxFromDp(6));

		mSelectedGradientDrawable = new GradientDrawable();
		mUnSelectedGradientDrawable = new GradientDrawable();

		mPadding_left = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_padding_left, (int) pxFromDp(3));
		mPadding_right = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_padding_right, (int) pxFromDp(3));
		mPadding_top = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_padding_top, (int) pxFromDp(0));
		mPadding_bottom = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_padding_bottom, (int) pxFromDp(0));

		mSelectedPadding_Left = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_selected_padding_left,
				(int) mPadding_left);
		mSelectedPadding_Right = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_selected_padding_right,
				(int) mPadding_right);
		mSelectedPadding_Top = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_selected_padding_top,
				(int) mPadding_top);
		mSelectedPadding_Bottom = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_selected_padding_bottom,
				(int) mPadding_bottom);

		mUnSelectedPadding_Left = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_unselected_padding_left,
				(int) mPadding_left);
		mUnSelectedPadding_Right = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_unselected_padding_right,
				(int) mPadding_right);
		mUnSelectedPadding_Top = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_unselected_padding_top,
				(int) mPadding_top);
		mUnSelectedPadding_Bottom = attributes.getDimensionPixelSize(
				R.styleable.BoxPagerIndicator_unselected_padding_bottom,
				(int) mPadding_bottom);

		mSelectedLayerDrawable = new LayerDrawable(
				new Drawable[] { mSelectedGradientDrawable });
		mUnSelectedLayerDrawable = new LayerDrawable(
				new Drawable[] { mUnSelectedGradientDrawable });

		setIndicatorStyleResource(mUserSetSelectedIndicatorResId,
				mUserSetUnSelectedIndicatorResId);
		setDefaultIndicatorShape(mIndicatorShape);
		setDefaultSelectedIndicatorSize(mDefaultSelectedWidth,
				mDefaultSelectedHeight, Unit.Px);
		setDefaultUnselectedIndicatorSize(mDefaultUnSelectedWidth,
				mDefaultUnSelectedHeight, Unit.Px);
		setDefaultIndicatorColor(mDefaultSelectedColor, mDefaultUnSelectedColor);
		setIndicatorVisibility(mVisibility);
		attributes.recycle();
	}

	public enum Shape {
		Oval, Rectangle
	}

	/**
	 * if you are using the default indicator, this method will help you to set
	 * the shape of indicator, there are two kind of shapes you can set, oval
	 * and rect.
	 * 
	 * @param shape
	 */
	public void setDefaultIndicatorShape(Shape shape) {
		if (mUserSetSelectedIndicatorResId == 0) {
			if (shape == Shape.Oval) {
				mSelectedGradientDrawable.setShape(GradientDrawable.OVAL);
			} else {
				mSelectedGradientDrawable.setShape(GradientDrawable.RECTANGLE);
			}
		}
		if (mUserSetUnSelectedIndicatorResId == 0) {
			if (shape == Shape.Oval) {
				mUnSelectedGradientDrawable.setShape(GradientDrawable.OVAL);
			} else {
				mUnSelectedGradientDrawable
						.setShape(GradientDrawable.RECTANGLE);
			}
		}
		resetDrawable();
	}

	/**
	 * Set Indicator style.
	 * 
	 * @param selected
	 *            page selected drawable
	 * @param unselected
	 *            page unselected drawable
	 */
	public void setIndicatorStyleResource(int selected, int unselected) {
		mUserSetSelectedIndicatorResId = selected;
		mUserSetUnSelectedIndicatorResId = unselected;
		if (selected == 0) {
			mSelectedDrawable = mSelectedLayerDrawable;
		} else {
			mSelectedDrawable = mContext.getResources().getDrawable(
					mUserSetSelectedIndicatorResId);
		}
		if (unselected == 0) {
			mUnselectedDrawable = mUnSelectedLayerDrawable;
		} else {
			mUnselectedDrawable = mContext.getResources().getDrawable(
					mUserSetUnSelectedIndicatorResId);
		}

		resetDrawable();
	}

	/**
	 * if you are using the default indicator , this method will help you to set
	 * the selected status and the unselected status color.
	 * 
	 * @param selectedColor
	 * @param unselectedColor
	 */
	public void setDefaultIndicatorColor(int selectedColor, int unselectedColor) {
		if (mUserSetSelectedIndicatorResId == 0) {
			mSelectedGradientDrawable.setColor(selectedColor);
		}
		if (mUserSetUnSelectedIndicatorResId == 0) {
			mUnSelectedGradientDrawable.setColor(unselectedColor);
		}
		resetDrawable();
	}

	public enum Unit {
		DP, Px
	}

	public void setDefaultSelectedIndicatorSize(float width, float height,
			Unit unit) {
		if (mUserSetSelectedIndicatorResId == 0) {
			float w = width;
			float h = height;
			if (unit == Unit.DP) {
				w = pxFromDp(width);
				h = pxFromDp(height);
			}
			mSelectedGradientDrawable.setSize((int) w, (int) h);
			resetDrawable();
		}
	}

	public void setDefaultUnselectedIndicatorSize(float width, float height,
			Unit unit) {
		if (mUserSetUnSelectedIndicatorResId == 0) {
			float w = width;
			float h = height;
			if (unit == Unit.DP) {
				w = pxFromDp(width);
				h = pxFromDp(height);
			}
			mUnSelectedGradientDrawable.setSize((int) w, (int) h);
			resetDrawable();
		}
	}

	public void setDefaultIndicatorSize(float width, float height, Unit unit) {
		setDefaultSelectedIndicatorSize(width, height, unit);
		setDefaultUnselectedIndicatorSize(width, height, unit);
	}

	@SuppressWarnings("unused")
	private float dpFromPx(float px) {
		return px
				/ this.getContext().getResources().getDisplayMetrics().density;
	}

	private float pxFromDp(float dp) {
		return dp
				* this.getContext().getResources().getDisplayMetrics().density;
	}

	/**
	 * set the visibility of indicator.
	 * 
	 * @param visibility
	 */
	public void setIndicatorVisibility(IndicatorVisibility visibility) {
		if (visibility == IndicatorVisibility.Visible) {
			setVisibility(View.VISIBLE);
		} else {
			setVisibility(View.INVISIBLE);
		}
		resetDrawable();
	}

	/**
	 * clear self means unregister the dataset observer and remove all the child
	 * views(indicators).
	 */
	public void destroySelf() {
		if (mPager == null || mPager.getAdapter() == null) {
			return;
		}
		IInfinitePagerAdapter wrapper = (IInfinitePagerAdapter) mPager
				.getAdapter();
		PagerAdapter adapter = wrapper.getRealAdapter();
		if (adapter != null) {
			adapter.unregisterDataSetObserver(dataChangeObserver);
		}
		removeAllViews();
	}

	/**
	 * bind indicator with viewpagerEx.
	 * 
	 * @param pager
	 */
	public void setViewPager(IViewPagerEx pager) {
		if (pager.getAdapter() == null) {
			throw new IllegalStateException(
					"Viewpager does not have adapter instance");
		}
		mPager = pager;
		mPager.addOnPageChangeListener(this);
		((IInfinitePagerAdapter) mPager.getAdapter()).getRealAdapter()
				.registerDataSetObserver(dataChangeObserver);
	}

	private void resetDrawable() {
		for (View i : mIndicators) {
			if (mPreviousSelectedIndicator != null
					&& mPreviousSelectedIndicator.equals(i)) {
				((ImageView) i).setImageDrawable(mSelectedDrawable);
			} else {
				((ImageView) i).setImageDrawable(mUnselectedDrawable);
			}
		}
	}

	/**
	 * redraw the indicators.
	 */
	public void redraw() {
		mItemCount = getShouldDrawCount();
		mPreviousSelectedIndicator = null;
		for (View i : mIndicators) {
			removeView(i);
		}

		for (int i = 0; i < mItemCount; i++) {
			ImageView indicator = new ImageView(mContext);
			indicator.setImageDrawable(mUnselectedDrawable);
			indicator.setPadding((int) mUnSelectedPadding_Left,
					(int) mUnSelectedPadding_Top,
					(int) mUnSelectedPadding_Right,
					(int) mUnSelectedPadding_Bottom);
			addView(indicator);
			mIndicators.add(indicator);
		}
		setItemAsSelected(mPreviousSelectedPosition);
	}

	/**
	 * since we used a adapter wrapper, so we can't getCount directly from
	 * wrapper.
	 * 
	 * @return
	 */
	private int getShouldDrawCount() {
		if (mPager.getAdapter() instanceof IInfinitePagerAdapter) {
			return ((IInfinitePagerAdapter) mPager.getAdapter()).getRealCount();
		} else {
			return mPager.getAdapter().getCount();
		}
	}

	private DataSetObserver dataChangeObserver = new DataSetObserver() {
		@Override
		public void onChanged() {
			PagerAdapter adapter = mPager.getAdapter();
			int count = 0;
			if (adapter instanceof IInfinitePagerAdapter) {
				count = ((IInfinitePagerAdapter) adapter).getRealCount();
			} else {
				count = adapter.getCount();
			}
			if (count > mItemCount) {
				for (int i = 0; i < count - mItemCount; i++) {
					ImageView indicator = new ImageView(mContext);
					indicator.setImageDrawable(mUnselectedDrawable);
					indicator.setPadding((int) mUnSelectedPadding_Left,
							(int) mUnSelectedPadding_Top,
							(int) mUnSelectedPadding_Right,
							(int) mUnSelectedPadding_Bottom);
					addView(indicator);
					mIndicators.add(indicator);
				}
			} else if (count < mItemCount) {
				for (int i = 0; i < mItemCount - count; i++) {
					removeView(mIndicators.get(0));
					mIndicators.remove(0);
				}
			}
			mItemCount = count;
			mPager.setCurrentItem(mItemCount * 20 + mPager.getCurrentItem());
		}

		@Override
		public void onInvalidated() {
			super.onInvalidated();
			redraw();
		}
	};

	private void setItemAsSelected(int position) {
		if (mPreviousSelectedIndicator != null) {
			mPreviousSelectedIndicator.setImageDrawable(mUnselectedDrawable);
			mPreviousSelectedIndicator.setPadding(
					(int) mUnSelectedPadding_Left,
					(int) mUnSelectedPadding_Top,
					(int) mUnSelectedPadding_Right,
					(int) mUnSelectedPadding_Bottom);
		}
		ImageView currentSelected = (ImageView) getChildAt(position + 1);
		if (currentSelected != null) {
			currentSelected.setImageDrawable(mSelectedDrawable);
			currentSelected.setPadding((int) mSelectedPadding_Left,
					(int) mSelectedPadding_Top, (int) mSelectedPadding_Right,
					(int) mSelectedPadding_Bottom);
			mPreviousSelectedIndicator = currentSelected;
		}
		mPreviousSelectedPosition = position;
	}

	@Override
	public void onPageScrolled(int position, float positionOffset,
			int positionOffsetPixels) {
	}

	public IndicatorVisibility getIndicatorVisibility() {
		return mVisibility;
	}

	@Override
	public void onPageSelected(int position) {
		if (mItemCount == 0) {
			return;
		}
		setItemAsSelected(position - 1);
	}

	@Override
	public void onPageScrollStateChanged(int state) {
	}

	public int getSelectedIndicatorResId() {
		return mUserSetSelectedIndicatorResId;
	}

	public int getUnSelectedIndicatorResId() {
		return mUserSetUnSelectedIndicatorResId;
	}

}
